package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ModelUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpDynamicInjectTaskDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpMultiInstanceTaskDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.CandidateVo;
import org.apache.commons.lang3.RandomStringUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.ProcessEngines;
import org.flowable.engine.common.impl.el.ExpressionManager;
import org.flowable.engine.common.impl.interceptor.Command;
import org.flowable.engine.common.impl.interceptor.CommandContext;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.cmd.AbstractDynamicInjectionCmd;
import org.flowable.engine.impl.dynamic.BaseDynamicSubProcessInjectUtil;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.*;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.springframework.beans.BeanUtils;

import java.util.*;

/**
 * 流程实例中动态新增流程节点
 *
 * @author : liuhuimin
 * @date : 2022/09/08 14:46
 */
public class DynamicInjectUserTaskInProcessInstanceService extends AbstractDynamicInjectionCmd implements Command<Void> {
    protected String processInstanceId;
    protected DynamicUserTaskBuilder dynamicUserTaskBuilder;
    protected FlowElement currentFlowElement;
    protected Boolean backWard;
    protected String oneVoteVetoType;
    protected HttpDynamicInjectTaskDto httpDynamicInjectTaskDto;
    /**
     * @param httpDynamicInjectTaskDto 流程实例ID
     * @param currentFlowElement       当前节点
     */
    public DynamicInjectUserTaskInProcessInstanceService(HttpDynamicInjectTaskDto httpDynamicInjectTaskDto, FlowElement currentFlowElement) {
        this.httpDynamicInjectTaskDto = httpDynamicInjectTaskDto;
        this.processInstanceId = httpDynamicInjectTaskDto.getProcessInstanceId();
        this.currentFlowElement = currentFlowElement;
        this.backWard = httpDynamicInjectTaskDto.getBackWard();
        dynamicUserTaskBuilder();
    }

    private void dynamicUserTaskBuilder(){
        DynamicUserTaskBuilder dynamicUserTaskBuilder = new DynamicUserTaskBuilder();
        dynamicUserTaskBuilder.setId(httpDynamicInjectTaskDto.getDynamicUserId());
        dynamicUserTaskBuilder.setName(httpDynamicInjectTaskDto.getDynamicNodeName());
        dynamicUserTaskBuilder.setAssignee(httpDynamicInjectTaskDto.getOperatorId());
        this.dynamicUserTaskBuilder = dynamicUserTaskBuilder;
    }
    @Override
    public Void execute(CommandContext commandContext) {
        // 将修改后的bpmnModel重新部署，并更新当前流程实例的proc_def_id
        createDerivedProcessDefinitionForProcessInstance(commandContext, processInstanceId);
        return null;
    }

    /**
     * 绘制新增节点，修改后续节点位置信息，并更新模板信息
     *
     * @param commandContext
     * @param process
     * @param bpmnModel
     * @param originalProcessDefinitionEntity
     * @param newDeploymentEntity
     */
    @Override
    protected void updateBpmnProcess(CommandContext commandContext, Process process,
                                     BpmnModel bpmnModel, ProcessDefinitionEntity originalProcessDefinitionEntity, DeploymentEntity newDeploymentEntity) {

        List<StartEvent> startEvents = process.findFlowElementsOfType(StartEvent.class);
        StartEvent initialStartEvent = null;
        for (StartEvent startEvent : startEvents) {
            if (startEvent.getEventDefinitions().size() == 0) {
                initialStartEvent = startEvent;
                break;

            } else if (initialStartEvent == null) {
                initialStartEvent = startEvent;
            }
        }
        if (currentFlowElement != null) {
            // 向后添加
            if (backWard) {
                dynamicInsertUserTaskBackward(initialStartEvent, process, bpmnModel);
            } else { // 向前添加
                dynamicInsertUserTaskForward(initialStartEvent, process, bpmnModel);
            }

        } else {
            dynamicInsertParallelTask(initialStartEvent, process, bpmnModel);
        }

        BaseDynamicSubProcessInjectUtil.processFlowElements(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
    }

    private void dynamicInsertUserTaskBackward(StartEvent initialStartEvent, Process process,
                                               BpmnModel bpmnModel) {
        UserTask userTask = buildDynamicUserTask(process);
        UserTask currentFlowElement = (UserTask) this.currentFlowElement;
        SequenceFlow sequenceFlow = null;

        List<SequenceFlow> outgoingFlows = new ArrayList<>();
        for (SequenceFlow sequenceFlow1 : currentFlowElement.getOutgoingFlows()) {
            sequenceFlow = new SequenceFlow(userTask.getId(), sequenceFlow1.getTargetRef());
            sequenceFlow.setSkipExpression(sequenceFlow1.getSkipExpression());
            sequenceFlow.setConditionExpression(sequenceFlow1.getConditionExpression());
            sequenceFlow.setExtensionElements(sequenceFlow1.getExtensionElements());
            sequenceFlow.setExecutionListeners(sequenceFlow1.getExecutionListeners());
            sequenceFlow.setName(sequenceFlow1.getName());
            // 设置唯一编号
            sequenceFlow.setId("id" + RandomStringUtils.random(8, true, true));
            outgoingFlows.add(sequenceFlow);
            //删除原先节点的出线
            process.removeFlowElement(sequenceFlow1.getId());
            //添加新的出线
            process.addFlowElement(sequenceFlow);
        }
        List<SequenceFlow> incomingFlows = new ArrayList<>();
        SequenceFlow incomingFlow = new SequenceFlow(currentFlowElement.getId(), userTask.getId());
        // 设置唯一编号
        incomingFlow.setId("id" + RandomStringUtils.random(8, true, true));
        incomingFlows.add(incomingFlow);
        //添加新的入线
        process.addFlowElement(incomingFlow);
        userTask.setOutgoingFlows(outgoingFlows);
        userTask.setIncomingFlows(incomingFlows);
        process.addFlowElement(userTask);

        //调整流程布局
        adjustLayOut(initialStartEvent, bpmnModel);
    }

    private UserTask buildDynamicUserTask(Process process) {
        UserTask userTask = new UserTask();
//        BeanUtils.copyProperties(currentFlowElement, userTask);
        // 动态节点编码
        if (dynamicUserTaskBuilder.getId() != null) {
            userTask.setId(dynamicUserTaskBuilder.getId());
        } else {
            userTask.setId(dynamicUserTaskBuilder.nextTaskId(process.getFlowElementMap()));
        }
        dynamicUserTaskBuilder.setDynamicTaskId(userTask.getId());
        userTask.setName(dynamicUserTaskBuilder.getName());

        // 节点候选人
        String[] assignees = httpDynamicInjectTaskDto.getOperatorId().split(",");
        // 会签节点
        if (httpDynamicInjectTaskDto.getIsMultiInstanceTask()) {
            HttpMultiInstanceTaskDto multiInstanceTaskDto = httpDynamicInjectTaskDto.getMultiInstanceTaskDto();
            if(StringUtils.isNotEmpty(multiInstanceTaskDto.getOneVoteVetoType())){
                oneVoteVetoType=multiInstanceTaskDto.getOneVoteVetoType();
            }
            // 配置会签所需条件
            MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
            // 配置节点人员办理顺序 串行:true 并行:false
            multiInstanceLoopCharacteristics.setSequential("Sequential".equalsIgnoreCase(multiInstanceTaskDto.getMultiInstanceType()));
            // 配置会签集合变量名称
            multiInstanceLoopCharacteristics.setInputDataItem(dynamicUserTaskBuilder.getDynamicTaskId());
            // 配置会签集合遍历名称
            multiInstanceLoopCharacteristics.setElementVariable("assignee");
            // 配置会签节点审批条件
            multiInstanceLoopCharacteristics.setCompletionCondition(multiInstanceTaskDto.getMultiInstanceCondition());
            // 节点设置会签循环特征
            userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
            // 设置节点处理人表达式 引用会签条件activiti:elementVariable="assignee"
            userTask.setAssignee("${assignee}");
            //4.设置任务监听器
            ModelUtils.getInstance().resolveTaskListener(userTask);
            //审批人员写入流程变量
            Map<String, Object> originVariables =new HashMap<>();
            originVariables.put(dynamicUserTaskBuilder.getDynamicTaskId(),Arrays.asList(assignees));
            CommandContextUtil.getProcessEngineConfiguration().getRuntimeService().setVariables(processInstanceId, originVariables);

        } else { // 普通节点

            userTask.setCandidateUsers(Arrays.asList(assignees));
            userTask.setLoopCharacteristics(null);
        }
        return userTask;
    }

    private void dynamicInsertUserTaskForward(StartEvent initialStartEvent, Process process,
                                              BpmnModel bpmnModel) {
        UserTask userTask = buildDynamicUserTask(process);
        UserTask currentFlowElement = (UserTask) this.currentFlowElement;
        SequenceFlow sequenceFlow = null;

        List<SequenceFlow> incomingFlows = new ArrayList<>();
        for (SequenceFlow incomeSequenceFlow : currentFlowElement.getIncomingFlows()) {
            sequenceFlow = new SequenceFlow(incomeSequenceFlow.getSourceRef(), userTask.getId());
            sequenceFlow.setSkipExpression(incomeSequenceFlow.getSkipExpression());
            sequenceFlow.setConditionExpression(incomeSequenceFlow.getConditionExpression());
            sequenceFlow.setExtensionElements(incomeSequenceFlow.getExtensionElements());
            sequenceFlow.setExecutionListeners(incomeSequenceFlow.getExecutionListeners());
            sequenceFlow.setName(incomeSequenceFlow.getName());
            // 设置唯一编号
            sequenceFlow.setId("id" + RandomStringUtils.random(8, true, true));
            incomingFlows.add(sequenceFlow);
            //删除原先节点的出线
            process.removeFlowElement(incomeSequenceFlow.getId());
            //添加新的出线
            process.addFlowElement(sequenceFlow);
        }

        List<SequenceFlow> outgoingFlows = new ArrayList<>();
        SequenceFlow outgoingFlow = new SequenceFlow(userTask.getId(), currentFlowElement.getId());
        // 设置唯一编号
        outgoingFlow.setId("id" + RandomStringUtils.random(8, true, true));
        outgoingFlows.add(outgoingFlow);
        //添加新的入线
        process.addFlowElement(outgoingFlow);
        userTask.setOutgoingFlows(incomingFlows);
        userTask.setIncomingFlows(outgoingFlows);
        process.addFlowElement(userTask);

        //调整流程布局
        adjustLayOut(initialStartEvent, bpmnModel);
    }

    private void adjustLayOut(StartEvent initialStartEvent, BpmnModel bpmnModel) {
        //新增坐标点
        GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(currentFlowElement.getId());
        if (elementGraphicInfo != null) {
            double yDiff = 0;
            double xDiff = 80;
            if (elementGraphicInfo.getY() < 173) {
                yDiff = 173 - elementGraphicInfo.getY();
                elementGraphicInfo.setY(173);
            }

            Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
            for (String locationId : locationMap.keySet()) {
                if (initialStartEvent.getId().equals(locationId)) {
                    continue;
                }

                GraphicInfo locationGraphicInfo = locationMap.get(locationId);
                locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
                locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
            }

            Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
            for (String flowId : flowLocationMap.keySet()) {
//                    if (flowFromStart.getId().equals(flowId)) {
//                        continue;
//                    }

                List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
                for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
                    flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
                    flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);

                }
            }
            // 调用自动排版方法,重绘流程图
            new BpmnAutoLayout(bpmnModel).execute();
        }

    }

    private void dynamicInsertParallelTask(StartEvent initialStartEvent, Process process,
                                           BpmnModel bpmnModel) {

        ParallelGateway parallelGateway = new ParallelGateway();
        parallelGateway.setId(dynamicUserTaskBuilder.nextForkGatewayId(process.getFlowElementMap()));
        process.addFlowElement(parallelGateway);

        UserTask userTask = new UserTask();
        if (dynamicUserTaskBuilder.getId() != null) {
            userTask.setId(dynamicUserTaskBuilder.getId());
        } else {
            userTask.setId(dynamicUserTaskBuilder.nextTaskId(process.getFlowElementMap()));
        }
        dynamicUserTaskBuilder.setDynamicTaskId(userTask.getId());

        userTask.setName(dynamicUserTaskBuilder.getName());
        userTask.setAssignee(dynamicUserTaskBuilder.getAssignee());
        process.addFlowElement(userTask);

        EndEvent endEvent = new EndEvent();
        endEvent.setId(dynamicUserTaskBuilder.nextEndEventId(process.getFlowElementMap()));
        process.addFlowElement(endEvent);

        SequenceFlow flowToUserTask = new SequenceFlow(parallelGateway.getId(), userTask.getId());
        flowToUserTask.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
        process.addFlowElement(flowToUserTask);

        SequenceFlow flowFromUserTask = new SequenceFlow(userTask.getId(), endEvent.getId());
        flowFromUserTask.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
        process.addFlowElement(flowFromUserTask);

        SequenceFlow initialFlow = initialStartEvent.getOutgoingFlows().get(0);
        initialFlow.setSourceRef(parallelGateway.getId());

        SequenceFlow flowFromStart = new SequenceFlow(initialStartEvent.getId(), parallelGateway.getId());
        flowFromStart.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
        process.addFlowElement(flowFromStart);
        //t调整节点的布局
        GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(initialStartEvent.getId());
        if (elementGraphicInfo != null) {
            double yDiff = 0;
            double xDiff = 80;
            if (elementGraphicInfo.getY() < 173) {
                yDiff = 173 - elementGraphicInfo.getY();
                elementGraphicInfo.setY(173);
            }

            Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
            for (String locationId : locationMap.keySet()) {
                if (initialStartEvent.getId().equals(locationId)) {
                    continue;
                }

                GraphicInfo locationGraphicInfo = locationMap.get(locationId);
                locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
                locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
            }

            Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
            for (String flowId : flowLocationMap.keySet()) {
                if (flowFromStart.getId().equals(flowId)) {
                    continue;
                }

                List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
                for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
                    flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
                    flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);
                }
            }

            // 调用自动排版方法,重绘流程图
            new BpmnAutoLayout(bpmnModel).execute();
        }
    }

    /**
     * 更新任务执行信息
     *
     * @param commandContext
     * @param processDefinitionEntity
     * @param processInstance
     * @param childExecutions
     */
    @Override
    protected void updateExecutions(CommandContext commandContext, ProcessDefinitionEntity processDefinitionEntity,
                                    ExecutionEntity processInstance, List<ExecutionEntity> childExecutions) {

        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
//        List<ExecutionEntity> oldExecution = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstance.getProcessInstanceId());
//        ExecutionEntity execution = executionEntityManager.createChildExecution(processInstance);
//        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionEntity.getId());
//
//        TaskService taskService = CommandContextUtil.getTaskService(commandContext);
//        List<TaskEntity> taskEntities = taskService.findTasksByProcessInstanceId(processInstanceId);
//        // 删除当前活动任务
//        for (TaskEntity taskEntity : taskEntities) {
//            taskEntity.getIdentityLinks().stream().forEach(identityLinkEntity -> {
//                if (identityLinkEntity.isGroup()) {
//                    taskEntity.deleteGroupIdentityLink(identityLinkEntity.getGroupId(), "candidate");
//                } else {
//                    taskEntity.deleteUserIdentityLink(identityLinkEntity.getUserId(), "participant");
//                }
//            });
//            if (taskEntity.getTaskDefinitionKey().equals(currentFlowElement.getId())) {
//
//                taskService.deleteTask(taskEntity, false);
//            }
//        }
//        //设置活动后的节点
//        UserTask userTask = (UserTask) bpmnModel.getProcessById(processDefinitionEntity.getKey()).getFlowElement(dynamicUserTaskBuilder.getId());
//        execution.setCurrentFlowElement(userTask);
//        Context.getAgenda().planContinueProcessOperation(execution);

//         对于一票否决类型的，动态新增会签节点时添加的自定义属性无法解析，这边手动处理
        if (StringUtils.isNotEmpty(oneVoteVetoType)) {
            ModelUtils.getInstance().resolveOneVoteVeto(httpDynamicInjectTaskDto.getDynamicUserId(),oneVoteVetoType, processDefinitionEntity.getDeploymentId());
        }
    }

}

